﻿using System;

namespace TestEffectiveCS
{
    //总是提供ToString()方法
    public class Test05
    {
        public class Customer:System.IFormattable
        {
            private string _name;
            public string Name { get { return _name; } }
            private decimal _revenue;
            public decimal Revenue { get { return _revenue; } }
            private string _contactPhone;
            public string ContactPhone { get { return _contactPhone; } }

            public Customer(string name, decimal revenue, string contactPhone)
            {
                _name = name;
                _revenue = revenue;
                _contactPhone = contactPhone;
            }

            public override string ToString()
            {
                return _name;
            }

            #region IFormattable 成员

            string IFormattable.ToString(string format, IFormatProvider formatProvider)
            {
                if (formatProvider != null)
                {
                    ICustomFormatter fmt = formatProvider.GetFormat(this.GetType()) as ICustomFormatter;
                    if (fmt != null)
                        return fmt.Format(format, this, formatProvider);
                }

                switch (format)
                {
                    case "r":
                        return _revenue.ToString();
                    case "p":
                        return _contactPhone;
                    case "nr":
                        return string.Format("{0,20}, {1,10:C}",
                            _name, _revenue);
                    case "np":
                        return string.Format("{0,20}, {0,15}", 
                            _name, _contactPhone);
                    case "nrp":
                        return string.Format("{0,-20}, {1,10:C}, {2,15}",
                            _name, _revenue, _contactPhone);
                    case "n":
                    case "G":
                    default:
                        return _name;
                }
            }

            #endregion
        }

        public class CustomerFormatter : IFormatProvider, ICustomFormatter
        {
            public object GetFormat(Type formatType)
            {
                if (formatType == typeof(ICustomFormatter))
                    return this;
                else
                    return null;
            }

            public string Format(string format,
                                  object arg,
                                  IFormatProvider formatProvider)
            {
                if (!this.Equals(formatProvider))
                {
                    return null;
                }
                else
                {
                    if (String.IsNullOrEmpty(format))
                        format = "G";

                    string customerString = arg.ToString();
                    if (customerString.Length < 8)
                        customerString = customerString.PadLeft(8, '0');

                    format = format.ToUpper();
                    switch (format)
                    {
                        case "G":
                            return customerString.Substring(0, 1) + "-" +
                                                  customerString.Substring(1, 5) + "-" +
                                                  customerString.Substring(6);
                        case "S":
                            return customerString.Substring(0, 1) + "/" +
                                                  customerString.Substring(1, 5) + "/" +
                                                  customerString.Substring(6);
                        case "P":
                            return customerString.Substring(0, 1) + "." +
                                                  customerString.Substring(1, 5) + "." +
                                                  customerString.Substring(6);
                        default:
                            throw new FormatException(
                                      String.Format("The '{0}' format specifier is not supported.", format));
                    }
                }
            }
        }


        public class CustomFormatter : IFormatProvider
        {
            #region IFormatProvider 成员

            public object GetFormat(Type formatType)
            {
                if (formatType == typeof(ICustomFormatter))
                    return new CustomerFormatProvider();
                return null;
            }

            #endregion
        }

        private class CustomerFormatProvider : ICustomFormatter
        {
            #region ICustomFormatter 成员

            public string Format(string format, object arg, IFormatProvider formatProvider)
            {
                Customer c = arg as Customer;
                if (c == null) return arg.ToString();
                return string.Format("{0,50}, {1,15}, {2,10:C}",
                    c.Name, c.ContactPhone, c.Revenue);
            }

            #endregion
        }

        public Test05()
        {
            IFormattable c = new Test05.Customer("Jack", 12345, "123456789123456");
            //Console.WriteLine(c.ToString("nrp", null));
            //Console.WriteLine();
            Console.WriteLine(string.Format(new Test05.CustomFormatter(), "{0}", c));

            int acctNumber = 79203159;
            Console.WriteLine(String.Format(new Test05.CustomerFormatter(), "{0:G}\n{1:S}\n{2:P}", 12345678, 87654321, 13572468));
            Console.WriteLine(String.Format(new Test05.CustomerFormatter(), "{0:G}", acctNumber));
            Console.WriteLine(String.Format(new Test05.CustomerFormatter(), "{0:S}", acctNumber));
            Console.WriteLine(String.Format(new Test05.CustomerFormatter(), "{0:P}", acctNumber));
            try
            {
                Console.WriteLine(String.Format(new Test05.CustomerFormatter(), "{0:X}", acctNumber));
            }
            catch (FormatException e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}